import React from "react";
import { connect } from "react-redux";
// import business from "../../../business";
import { isEmpty, isNil, mapValues } from "lodash";
import { withRouter } from "react-router-dom";
import { createAction } from "redux-actions";
import { Dispatch } from "redux";
// import commonAction from "../../common/action";
import Selector from "../../reselector";
import { message as messager } from "antd";

interface GenericContainer {
  navigateTo(path: string, param: any): any;
  pathTo(path: string): any;
}

interface GenericContainerProps {
  history: { push(path: string): any };
  pattern: string;
  allowedFunctions: string[];
  cache: any[];
  Component: any;
  merger: object;
  SESSION: any;
  MENUS: Array<{ pattern: any }>;
  match: any;
  getComponentParam(path): any;
  SELECTMENU(target: { pattern: any }): any;
  SETROUTERPARAM(data: { pattern: string; param: any }): any;
}

const defaultAction = message => message;
class GenericContainer extends React.Component<GenericContainerProps, any> {
  constructor(props) {
    super(props);
    this.navigateTo = (pattern, param) => {
      if (pattern && typeof pattern === "string") {
        const { history, SETROUTERPARAM } = this.props;
        if (isNil(param)) {
          history.push(`/dashboard/${pattern.replace(".", "/")}`);
        } else {
          SETROUTERPARAM({ pattern, param });
          history.push(`/dashboard/${pattern.replace(".", "/")}`);
        }
      } else {
        const { MENUS, history } = this.props;
        if (MENUS[0]) {
          // TODO 应该把第一个菜单设置成App
          history.push(`/dashboard/${MENUS[0].pattern.replace(".", "/")}`);
          // history.push(`/dashboard/app`);
        } else {
          messager.error("您已登录，但目前没有任何菜单权限");
        }
      }
    };
    this.pathTo = path => {
      const { history } = this.props;
      history.push(path);
    };
    // this.allowedFunction  =  Array.isArray(buttons) ? buttons.map(r => r.id):[]
  }

  public componentDidMount() {
    const { SELECTMENU, pattern, SESSION } = this.props;
    if (!isNil(SESSION)) {
      SELECTMENU({ pattern });
    }
  }

  public componentWillUnmount() {
    const { SETROUTERPARAM, pattern, getComponentParam } = this.props;
    const param = getComponentParam(pattern);
    if (!isNil(param) && !isEmpty(param)) {
      SETROUTERPARAM({ pattern, param: undefined });
    }
  }

  public render() {
    const {
      Component,
      allowedFunctions,
      SESSION,
      merger,
      cache,
      pattern,
      match,
      getComponentParam
    } = this.props;
    const { params = {} } = match;
    const extension = {
      navigateTo: this.navigateTo,
      pathTo: this.pathTo,
      allowedFunctions
    };
    const childProps = { ...merger, ...extension };
    const cacheParam = getComponentParam(pattern) || {};
    return (
      <Component
        {...childProps}
        SESSION={SESSION}
        cache={cache}
        param={{ ...cacheParam, ...params }}
      />
    );
  }
}
export default async options => {
  const { entry:srcEntry, globalAction } = options;
  const business = await require(srcEntry);

  function returnConnectedContainer(container) {
    const newContainers = {};
    const promises = [] as any[];
    const result = Object.keys(business).map((r, i) => {
      return new Promise(res => {
        promises.push(res);
      });
    });
    const rawActions = {};
    Object.keys(business).forEach(async (name, i) => {
      const required = await Promise.all([
        require(`${srcEntry}/${name}/action`),
        require(`${srcEntry}/${name}/props`),
        require(`${srcEntry}/${name}`)
      ]);
      const { exact, pattern, param } = business[name];
      newContainers[pattern] = (([mAction, mProps, Component]) => {
        let mapStateToPropsArray = Object.keys(globalAction).map(
          r => globalAction[r].mapStateToProps
        );
        if (!isNil(mProps.default.mapStateToProps)) {
          mapStateToPropsArray = [
            ...mapStateToPropsArray,
            mProps.default.mapStateToProps
          ];
        }
        const mapStateToProps = (state, ownProps) => {
          return {
            cache: mapValues(Selector, v => v(state)),
            ...mapStateToPropsArray
              .map(r => r(state, ownProps))
              .reduce((accumulator, currentValue) =>
                Object.assign(accumulator, currentValue)
              )
          };
        };

        const mapDispatchToProps = (dispatch: Dispatch<any>) => {
          const newActions = {};
          Object.keys(mAction.default).forEach(r => {
            if (!isNil(r)) {
              // 按照 `模块名 + 点 + action名进行约定性命名`，此处是对action进行规约
              const actionType = `${name}.${r}`;
              const rawAction = createAction(
                actionType,
                mAction.default[r].payloadCreator || defaultAction
              ) as any;
              rawActions[actionType] = rawAction;
              newActions[r] = data => dispatch(rawAction(data));
              if (!isNil(mAction.default[r].customActionDispatcher)) {
                Object.assign(
                  newActions,
                  mAction.default[r].customActionDispatcher(rawActions)
                );
              }
            }
          });
          Object.keys(globalAction).forEach(r => {
            const scope = globalAction[r];
            Object.keys(globalAction[r].actions).forEach(t => {
              const actionType = `global.${r}.${t}`;
              const localAction = createAction(
                actionType,
                scope.actions[t].payloadCreator || defaultAction
              ) as any;
              rawActions[actionType] = localAction;
              newActions[t.toUpperCase()] = data => dispatch(localAction(data));
            });
            // mapStateToPropsArray.push(scope.mapStateToProps);
          });
          return newActions;
        };

        let entry = "";
        let subentry = "";
        if (!isEmpty(pattern)) {
          if (/^\w+\.\w+/.test(pattern)) {
            const splitEntry = pattern.split(".");
            entry = splitEntry[0];
            subentry = splitEntry[1];
          } else {
            entry = pattern;
          }
        }

        const rawComponent = withRouter(
          connect(
            mapStateToProps,
            mapDispatchToProps,
            (stateProps, dispatchProps, ownProps) => {
              return Object.assign(
                {
                  merger: { ...stateProps, ...dispatchProps },
                  pattern,
                  Component: Component.default
                },
                ownProps,
                stateProps,
                dispatchProps
              );
            }
          )(container)
        );
        return {
          path: `/${entry}${subentry && `/${subentry}`}`,
          exact,
          param,
          component: rawComponent
        };
      })(required);
      promises[i]();
    });

    return Promise.all(result).then(() => newContainers);
  }

  return returnConnectedContainer(GenericContainer) as any;
};